#include "CMap.h"

#include <fstream>
#include <string>
#include <sstream>
#include <cassert>

#include "myexception.h"

#include "CGameObject.h"

using namespace std;

CMap::CMap(const string& fname)
{
	fprintf(stderr, "loading map: %s\n", fname.c_str());
	string s;
	ifstream fin(fname.c_str());

	if (!fin)
        throw MyException("Error loading map");

	// Get Width and Heigth;
	size_t num_levels = 0;
	{
		getline(fin,s);
		istringstream ss(s);
		ss >> Width;
		ss >> Height;
		ss >> num_levels;
	}

	Map.reserve(Width * Height);

	int j = 0, k = 0;
	while (getline(fin, s))
		if (s[0] != '#') // skip comments
		{
			int i = 0;
			istringstream ss(s);
			string o;
			while (ss >> o)
			{
				CGameObject* po = 0;
				CCharacter* cpo = 0;
				++i;
				if (o == "DB")
					po = new CDirtBlock;
				else if(o == "BB")
					po = new CBrownBlock;
				else if(o == "PB")
					po = new CPlainBlock;
				else if(o == "GB")
					po = new CGrassBlock;
				else if(o == "WB")
					po = new CWaterBlock;
				else if(o == "WOODB")
					po = new CWoodBlock;
				else if(o == "0")
					po = 0;
				else if(o == "CB")
					cpo = new CCharacterBoy;
				else if(o == "CCG")
					cpo = new CCharacterCatGirl;
				else if(o == "CHG")
					cpo = new CCharacterHornGirl;
				else if(o == "CPG")
					cpo = new CCharacterPinkGirl;
				else if(o == "CPRG")
					cpo = new CCharacterPrincessGirl;
				else
					throw MyException("Unknown object type.");

				if (cpo)
				{
					po = cpo;
					Characters.push_back(cpo);
				}

				if (k == 0)
				{
					Map.push_back(vector<CGameObject*>());
					if (po)
						Map.back().push_back(po);
				} else {
					if (po)
						At(i-1,j).push_back(po);
				}
			}

			if (i != Width)
				throw MyException("Error loading map: i != width");
			++j;

			if (j == Height)
			{
				++k;
				j = 0;
				i = 0;
			}
		}

	if (j)
	{
		printf("j = %ld, height = %ld\n", j, Height);
		throw MyException("Error loading map: j != height");
	}
	
}

CMap::~CMap()
{
//	fprintf(stderr, "free map.\n");
	for (vector< vector<CGameObject*> >::iterator j = Map.begin(); j != Map.end(); ++j)
		for (vector<CGameObject*>::iterator i = j->begin(); i != j->end(); ++i)
			delete *i;
#if 0
	for(vector< vector<int> >::const_iterator j = Map.begin(); j != Map.end(); ++j) {
		for (vector<int>::const_iterator i = j->begin(); i != j->end(); ++i)
			printf("%d ", *i);
		printf("\n");
	}
#endif

}

size_t CMap::GetHeight() const
{
	return Height;
}

size_t CMap::GetWidth() const
{
	return Width;
}

vector<CGameObject*>& CMap::At(size_t x, size_t y) 
{
	assert(x < Width && y < Height);
	if ( y * Width + x >= Map.size())
		printf("map size = %ld, x = %ld, y = %ld\n",
		Map.size(),
		x, y);

	return Map[y * Width + x];
}

CGameObject* CMap::At(size_t x, size_t y, size_t z)
{
	vector<CGameObject*> ol = At(x,y);
	if ( z > ol.size())
		return 0;
	return ol[z];
}


size_t ShadowLength(const vector<CGameObject*>& ol)
{
	return ol.back()->IsDropShadow() ? ol.size() - 1 : ol.size() - 2;
}
void CMap::Shadow(vector<char>& ret, size_t x, size_t y, size_t z)
{
	ret.erase(ret.begin(), ret.end());
	if (!At(x,y,z)->IsDropShadow())
		return;

	vector<CGameObject*> *ol = &At(x,y);
	vector<CGameObject*> *south = 0,
		*south_east = 0,
		*south_west = 0,
		*east = 0,
		*west = 0,
		*north = 0,
		*north_east = 0,
		*north_west = 0;


	if (x > 0)
		west = &At(x - 1, y);
	if (y > 0)
		north = &At(x, y - 1);
	if (y < Height - 1)
		south = &At(x, y + 1);
	if (x < Width - 1)
		east = &At(x + 1, y);
	if (x > 0 && y > 0)
		north_west = &At(x - 1, y - 1);
	if (x > 0 && y < Height - 1)
		south_west = &At(x - 1, y + 1);
	if (x < Width - 1 && y > 0)
		north_east = &At(x + 1, y - 1);
	if (x < Width - 1 && y < Height - 1)
		south_east = &At(x + 1, y + 1);

	if (z == ShadowLength(*ol))
	{
		// we on top block
		if (south_east && 
			!south_east->empty() &&
			ShadowLength(*south_east) > z &&
			(east && (east->empty() || ShadowLength(*east) <= z)))
			ret.push_back(SHADOW_SOUTH_EAST);
		if (south && 
			!south->empty() && 
			ShadowLength(*south) > z)
			ret.push_back(SHADOW_SOUTH);
		if (south_west && 
			!south_west->empty() && 
			ShadowLength(*south_west) > z && 
			(west && (west->empty() || ShadowLength(*west) <= z)))
			ret.push_back(SHADOW_SOUTH_WEST);
		if (east && 
			!east->empty() &&
			ShadowLength(*east) > z)
			ret.push_back(SHADOW_EAST);
		if (west &&
			!west->empty() &&
			ShadowLength(*west) > z)
			ret.push_back(SHADOW_WEST);
		if (north_east &&
			!north_east->empty() &&
			ShadowLength(*north_east) > z &&
			(north && (north->empty() || ShadowLength(*north) <= z)))
			ret.push_back(SHADOW_NORTH_EAST);
		if (north &&
			!north->empty() &&
			ShadowLength(*north) > z)
			ret.push_back(SHADOW_NORTH);
		if (north_west &&
			!north_west->empty() &&
			ShadowLength(*north_west) > z &&
			(north && (north->empty() || ShadowLength(*north) <= z)))
			ret.push_back(SHADOW_NORTH_WEST);
	}

	if (south_west &&
		!south_west->empty() &&
		ShadowLength(*south_west) >= z)
		ret.push_back(SHADOW_SIDE_WEST);
}

CGameObject* CMap::GetBlock(size_t x, size_t y)
{
	vector<CGameObject*> &ol = At(x,y);
	if (ol.empty())
		return 0;

	CGameObject* o = ol.back();
	ol.pop_back();
	return o;
}

void CMap::SetBlock(CGameObject *go, size_t x, size_t y)
{
	if (!go)
		return;
	vector<CGameObject*> &ol = At(x,y);
	ol.push_back(go);
}
bool CMap::CouldGetBlock(size_t x, size_t y)
{
	vector<CGameObject*> *ol = &At(x,y);
	if (ol->empty())
		return false;
	return ol->back()->IsMovable();
}

bool CMap::CouldSetBlock(size_t x, size_t y)
{
	vector<CGameObject* > *v = &At(x,y);
	if (!v->empty())
		return v->back()->CanSetBlock();
	else
		return true;
}
